package com.runa.monitor.platinterface.netservice.equipment;

import com.alibaba.fastjson.JSON;
import com.runa.monitor.comm.dto.Archive;
import com.runa.monitor.comm.dto.Node;
import com.runa.monitor.comm.dto.newComm.ValveVO;
import com.runa.monitor.platinterface.backwork.function.TaskFunction;
import com.runa.monitor.platinterface.backwork.impl.SelfTaskFunction;
import com.runa.monitor.platinterface.dispatch.conn.gen.RunaEquArchive;
import com.runa.monitor.platinterface.netservice.connect.commstatus.CommStatus;
import com.runa.persistence.CollectionUtil;
import com.runa.persistence.StringUtil;
import com.runa.persistence.task.RequestPendingTask;
import com.runa.persistence.task.TaskTypeEnum;
import com.runa.protocol.dto.ModbusPoint;
import com.runa.protocol.dto.ModbusWriteRequestData;
import com.runa.protocol.enums.ByteOrder;
import com.runa.protocol.enums.ModbusDataType;
import com.runa.protocol.enums.ModbusFunctionCode;
import com.runa.protocol.enums.ModbusType;
import com.runa.protocol.util.ByteUtil;
import com.runa.rccprotocol.dto.ConcentratorRequestData;
import com.runa.rccprotocol.dto.equdata.EquData;
import com.runa.rccprotocol.dto.equdata.TransForwardReply;
import com.runa.rccprotocol.dto.param.*;
import com.runa.rccprotocol.dto.rn.RunaBuildValveRequest;
import com.runa.rccprotocol.dto.runa.ModbusRequestData;
import com.runa.rccprotocol.dto.runa.RunaConcenAffirmDeny;
import com.runa.rccprotocol.dto.runa.RunaConcenRequestData;
import com.runa.rccprotocol.dto.runa.RunaDeviceRequestData;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public class Concentrator extends NetEqu<String> {


    private final String electricityMeter;

    public Concentrator(String serial, Integer timeout, String electricityMeter) {
        super(serial, timeout);
        this.heartbeatTimeout = 10 * 60 * 1000;
        this.electricityMeter = electricityMeter;
    }

    @Override
    public void readNodeEqu(List<Node> nodes, TaskFunction function, EquData.EquDataType type) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, nodes);
            return;
        }

        switch (type) {
            case METER:
                readMeter(nodes, function, type);
                break;
            case VALVE:
                readValve(nodes, function, type);
                break;
            case PUBLIC_UNIT:
                readBuildValve(nodes, function, type);
                break;
            default:
                readRuna(nodes, function, type);
        }
    }

    private void readBuildValve(List<Node> nodes, TaskFunction function, EquData.EquDataType type) {
        List<Node> nodeList = new ArrayList<>();
        for (Node node : nodes) {
            if ("RU_FORWARD".equals(node.getModel())) {
                RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                ModbusRequestData modbusRequestData = new ModbusRequestData();
                modbusRequestData.read((byte)(node.getIndex() & 0xff), (byte)0x03, 0, 35);
                requestData.transparentForwarding(node.getPort(), "2400", "E", 2, 20, modbusRequestData.getData());
                sendRequest(requestData, function, new RequestPendingTask(TaskTypeEnum.READ_RN_MODBUS_VALVE, node));
            } if ("RU_FORWARD_188".equals(node.getModel())) {
                byte[] bytes = RunaBuildValveRequest.read188(node.getSerial(), node.getIndex());
                RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                requestData.transparentForwarding(node.getPort(), "2400", "E", 2, 20, bytes);
                sendRequest(requestData, function, new RequestPendingTask(TaskTypeEnum.READ_RN_188_VALVE, node));

            } else {
                nodeList.add(node);
            }
        }
        readRuna(nodeList, function, type);
    }

    public void readMeter(List<Node> nodes, TaskFunction function, EquData.EquDataType type) {
        List<Node> nodeList = new ArrayList<>();
        for (Node node : nodes) {
            if ("汉阳户表".equalsIgnoreCase(node.getModel())) {
                RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                requestData.readHyMeter(node.getSerial(), node.getPort());
                requestData.setRemain(Collections.singletonList(node));
                sendRequest(requestData, function, TaskTypeEnum.READ_HY_METER);
            } else if ("RU_FORWARD".equals(node.getModel())) {
                RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                RunaDeviceRequestData runaDeviceRequestData = new RunaDeviceRequestData();
                runaDeviceRequestData.readHeatMeter(node.getSerial());
                requestData.transparentForwarding(node.getPort(), "2400", "E", 2, 20, runaDeviceRequestData.getData());
                sendRequest(requestData, function);
            } else {
                nodeList.add(node);
            }
        }
        readRuna(nodeList, function, type);
    }

    public void readValve(List<Node> nodes, TaskFunction function, EquData.EquDataType type) {
        List<Node> nodeList = new ArrayList<>();
        for (Node node : nodes) {
            if ("TF-LM-V".equalsIgnoreCase(node.getModel())) {
                RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                requestData.readWkxValve(node.getSerial(), node.getPort());
                requestData.setRemain(Collections.singletonList(node));
                sendRequest(requestData, function, TaskTypeEnum.READ_WKX_VALVE);
            } else if ("ZF-Y".equals(node.getModel())) {
                RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                requestData.readKtValve(node.getSerial(), node.getPort());
                requestData.setRemain(Collections.singletonList(node));
                sendRequest(requestData, function, TaskTypeEnum.READ_KT_VALVE);
            } else if ("DDF3-12133".equals(node.getModel())) {
                RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                requestData.readPsValve(node.getSerial(), node.getPort());
                requestData.setRemain(Collections.singletonList(node));
                sendRequest(requestData, function, TaskTypeEnum.READ_PS_VALVE);

            } else if ("RU_VC_FORWARD".equals(node.getModel())) {
                RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                RunaDeviceRequestData runaDeviceRequestData = new RunaDeviceRequestData();
                runaDeviceRequestData.readHouseValve(node.getSerial());
                requestData.transparentForwarding(node.getPort(), "2400", "E", 2, 20, runaDeviceRequestData.getData());
                sendRequest(requestData, function);
            } else {
                nodeList.add(node);
            }
        }
        readRuna(nodeList, function, type);
    }

    public void readRuna(List<Node> nodes, TaskFunction function, EquData.EquDataType type) {
        if (CollectionUtil.isEmpty(nodes)) {
            return;
        }
        List<ConcentratorRequestData> requestDatas = createRequestByNode(nodes, type);
        log.debug("集中器:{} 开始下发节点设备读取指令...", serial());
        for (ConcentratorRequestData request : requestDatas) {
            sendRequest(request, function);
        }
    }

    public void setUpperTime(Date date, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, null);
            return;
        }
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.timing(date);
        sendRequest(requestData, function);
    }

    public void readElectricityMeter() {
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        String[] params = electricityMeter.split(",");
        int port = Integer.parseInt(params[0]);
        RunaDeviceRequestData data = new RunaDeviceRequestData(serial());
        data.readElectricMeter(params[3]);
        sendForwardString(null, port, params[1], params[2], data.getData(), new SelfTaskFunction(serial()));
    }

    public List<ConcentratorRequestData> createRequestByNode(List<Node> nodes, EquData.EquDataType dataType) {
        List<Integer> pns = new ArrayList<>();
        List<Node> remains = new ArrayList<>();
        List<ConcentratorRequestData> requests = new ArrayList<>();
        RunaConcenRequestData requestData;
        if (nodes.size() < 11 && !nodes.isEmpty()) {
            for (Node node : nodes) {
                pns.add(node.getIndex());
                remains.add(node);
            }
            requestData = setPns(dataType, pns);
            requestData.setRemain(remains);
            requests.add(requestData);
        } else {
            for (Node node : nodes) {
                pns.add(node.getIndex());
                remains.add(node);
                if (pns.size() == 10) {
                    requestData = setPns(dataType, pns);
                    requestData.setRemain(remains);
                    requests.add(requestData);
                    pns.clear();
                    remains = new ArrayList<>();
                }
            }
            if (!pns.isEmpty()) {
                requestData = setPns(dataType, pns);
                requestData.setRemain(remains);
                requests.add(requestData);
            }
        }
        return requests;
    }

    private RunaConcenRequestData setPns(EquData.EquDataType dataType, List<Integer> pns) {
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        switch (dataType) {
            case METER:
                requestData.heatMeterData(pns);
                break;
            case VALVE:
                requestData.valveData(pns);
                break;
            case TEMPERATURE_PANEL:
                requestData.measurTemperaturePanelData(pns);
                break;
            case PUBLIC_UNIT:
                requestData.publicUnitData(pns);
                break;
            default:
        }
        return requestData;
    }

    @Override
    public void readNodeRecord(List<Integer> indexes, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, indexes);
            return;
        }
        List<ConcentratorRequestData> requestDatas = new ArrayList<>();
        List<Integer> pns = new ArrayList<>();
        RunaConcenRequestData requestData;
        for (Integer integer : indexes) {
            pns.add(integer);
            if (pns.size() == 20) {
                requestData = new RunaConcenRequestData(serial());
                requestData.readParameter(pns);
                requestData.setRemain(pns);
                requestDatas.add(requestData);
                pns = new ArrayList<>();
            }
        }
        if (!pns.isEmpty()) {
            requestData = new RunaConcenRequestData(serial());
            requestData.readParameter(pns);
            requestDatas.add(requestData);
            requestData.setRemain(pns);
        }

        log.info("集中器:{} 开始下发读取档案指令...", serial());
        for (ConcentratorRequestData data : requestDatas) {
            sendRequest(data, function);
        }
    }

    @Override
    public void setNodeEquArchive(List<Archive> archives, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine,
                    archives.stream().map(archive ->
                    ((RunaEquArchive) archive).getIndex())
                            .collect(Collectors.toList()));
        }
        List<Integer> index = new ArrayList<>();
        List<ConcentratorRequestData> requestDatas = new ArrayList<>();
        List<IndexRecord> records = new ArrayList<>();
        RunaConcenRequestData requestData;
        for (Archive archive : archives) {
            RunaEquArchive meterArchive = (RunaEquArchive) archive;
            IndexRecord record = new IndexRecord();
            record.setAddress(meterArchive.getSerial());
            record.setDoorplate(StringUtils.isEmpty(meterArchive.getSid()) ? "000000000000" : meterArchive.getSid());
            record.setIndex(meterArchive.getIndex());
            record.setPort(meterArchive.getPort());
            record.setCode(meterArchive.getProtocol());
            if (meterArchive.getHeatArea() != null) {
                record.setArea(meterArchive.getHeatArea());
            }
            record.setType(meterArchive.getType());
            if (meterArchive.getApportion() != null) {
                record.setFt(meterArchive.getApportion());
            }
            record.setSign(meterArchive.getSmallClass());
            records.add(record);
            index.add(meterArchive.getIndex());
            if (records.size() == 10) {
                requestData = new RunaConcenRequestData(serial());
                requestData.setParameter(records);
                requestData.setRemain(index);
                requestDatas.add(requestData);
                index = new ArrayList<>();
                records.clear();
            }
        }
        if (!records.isEmpty()) {
            requestData = new RunaConcenRequestData(serial());
            requestData.setParameter(records);
            requestData.setRemain(index);
            requestDatas.add(requestData);
        }
        log.info("集中器:{} 开始下发档案指令... {}", serial(), requestDatas.size());
        log.debug("requestDatas: {}", JSON.toJSONString(requestDatas));
        for (ConcentratorRequestData request : requestDatas) {
            sendRequest(request, function);
        }
    }

    @Override
    public void clearNodeEquArchive(TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, null);
            return;
        }
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.fmuRecordClear();

        log.info("集中器:{} 开始下发清除档案指令...", serial());
        sendRequest(requestData, function);
    }

    public void readingNodeDataNow(TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, null);
            return;
        }
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.immediatelyRead();
        log.info("集中器:{} 开始下发立即抄表指令...", serial());
        sendRequest(requestData, function);
    }

    @Override
    public void valveOnOffControl(List<Node> valves, boolean ifLock, Integer opening, TaskFunction function) {
        try {
            if (!online()) {
                function.doApply(CommStatus.OffLine, valves);
                return;
            }
            Map<Integer, ValveLock> valveLock;
            for (Node valve : valves) {
                if ("TF-LM-V".equalsIgnoreCase(valve.getModel())) {
                    RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                    requestData.setWkxValveOpen(valve.getSerial(), valve.getPort(), opening, ifLock);
                    requestData.setRemain(valve);
                    sendRequest(requestData, function, TaskTypeEnum.WRITE_WKX_VALVE);
                } else if ("ZF-Y".equals(valve.getModel())) {
                    RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                    requestData.setKtValveOpen(valve.getSerial(), valve.getPort(), opening, ifLock);
                    requestData.setRemain(valve);
                    sendRequest(requestData, function);
                } else if ("DDF3-12133".equals(valve.getModel())) {
                    RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                    requestData.setPsValveOpen(valve.getSerial(), valve.getPort(), opening, ifLock);
                    requestData.setRemain(valve);
                    sendRequest(requestData, function, TaskTypeEnum.WRITE_PS_VALVE);

                } else if ("RU_VC_FORWARD".equals(valve.getModel()) || "RU_FORWARD".equals(valve.getModel())) {
                    RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                    RunaDeviceRequestData runaDeviceRequestData = new RunaDeviceRequestData();
                    runaDeviceRequestData.setHouseValveOpen(valve.getSerial(), opening, ifLock);
                    requestData.transparentForwarding(valve.getPort(), "2400", "E", 2, 20, runaDeviceRequestData.getData());
                    sendRequest(requestData, function);
                } else {
                    valveLock = new HashMap<>();
                    valveLock.put(valve.getIndex(), new ValveLock(ifLock ? 1 : 0, opening == null ? 0xff : opening));
                    RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                    requestData.houseOnOffWriteValveLock(valveLock);
                    log.info("集中器:{} 开始下发阀门控制指令...", serial());
                    requestData.setRemain(valve);
                    sendRequest(requestData, function);
                }
            }
        } catch (Exception e) {
            log.error("集中器:{}下发阀门控制指令失败", e.toString());
        }
    }

    public void valveTemperatureLock(List<Node> valves, boolean ifLock, float temperature, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valves);
            return;
        }
        Map<Integer, TemperatureLock> temperatureLock;
        for (Node v : valves) {
            temperatureLock = new HashMap<>(1);
            temperatureLock.put(v.getIndex(), new TemperatureLock(ifLock ? 1 : 0, temperature));
            RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
            requestData.houseOnOffWriteTempLock(temperatureLock);

            log.info("集中器:{} 开始下发阀门温度指令...", serial());
            requestData.setRemain(v);
            sendRequest(requestData, function);
        }
    }

    public void valveTemperatureLimit(List<Node> valves, float up, float down, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valves);
            return;
        }
        Map<Integer, TemperatureRange> temperatureRange;
        for (Node v : valves) {
            temperatureRange = new HashMap<>();
            temperatureRange.put(v.getIndex(), new TemperatureRange(up, down));
            RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
            requestData.houseOnOffWriteTempRange(temperatureRange);

            log.info("集中器:{} 开始下发阀门温度上下限指令...", serial());
            requestData.setRemain(v);
            sendRequest(requestData, function);
        }
    }

    public void buildValveForward(Node valve, Integer model, Map param, TaskFunction function) {
        byte[] forwardBytes = null;
        log.debug("{} {} {}", valve.getSerial(), model, JSON.toJSONString(param));
        switch (model) {
            case 1:
                ValveVO.ModelEnum controlObject = ValveVO.ModelEnum.valueOf(String.valueOf(param.get("controlObject")));
                Float targetValue = Float.valueOf(String.valueOf(param.get("targetValue")));
                switch (controlObject) {
                    case 开度:
                        if ("RU_FORWARD".equals(valve.getModel())) {
                            forwardBytes = RunaBuildValveRequest.openControl(valve.getIndex(), targetValue.intValue());
                        } else if ("RU_FORWARD_188".equals(valve.getModel())) {
                            forwardBytes = RunaBuildValveRequest.openControl188(valve.getSerial(),  valve.getIndex(), targetValue.intValue());
                        }
                        break;
                }
                break;
            case 4:
                Float valveUp = Float.valueOf(String.valueOf(param.get("valveUp")));
                Float valveDown = Float.valueOf(String.valueOf(param.get("valveDown")));
                if ("RU_FORWARD".equals(valve.getModel())) {
                    forwardBytes = RunaBuildValveRequest.openLimit(valve.getIndex(), valveUp.intValue(), valveDown.intValue());
                } else if ("RU_FORWARD_188".equals(valve.getModel())) {
                    forwardBytes = RunaBuildValveRequest.openLimit188(valve.getSerial(), valve.getIndex(), valveUp.intValue(), valveDown.intValue());
                }
                break;
        }
        if (null == forwardBytes) {
            log.error("forwardBytes is null");
            return;
        }
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.setRemain(valve);
        requestData.transparentForwarding(valve.getPort(), "2400", "E", 2, 20, forwardBytes);
        sendRequest(requestData, function, new RequestPendingTask(TaskTypeEnum.READ_RN_MODBUS_VALVE, valve));
    }

    @Override
    public void sentPublicUnitRecord(List<Node> valves, Integer model, Map param, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valves);
            return;
        }
        for (Node unit : valves) {
            if (StringUtil.isNotBlank(unit.getModel()) && unit.getModel().startsWith("RU_FORWARD")) {
                buildValveForward(unit, model, param, function);
            } else {
                RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
                switch (model) {
                    case 1:
                        PublicUnitAdjustStrategy strategy = new PublicUnitAdjustStrategy();
                        strategy.setHandAuto(1);
                        strategy.setControlObject(controlObject((String) param.get("controlObject")));//controlObject: 1阀门开度、2室内温度、3热表流速、4热表进回水温差、5热表回水温度、6热表进水温度
                        strategy.setSetting(Float.parseFloat(param.get("targetValue").toString()));
                        requestData.sentPuasParameter(unit.getIndex(), strategy);
                        break;
                    case 2:
                        List<Map<String, Float>> schemes = new ArrayList<>();
                        Map<String, Float> scheme;
                        for (int i = 1; i <= 3; i++) {
                            scheme = new LinkedHashMap<>(6);
                            Map plan = (Map) param.get("Plans" + i);
                            scheme.put(plan.get("Time1").toString(), Float.parseFloat((String) plan.get("Temperature1")));
                            scheme.put(plan.get("Time2").toString(), Float.parseFloat((String) plan.get("Temperature2")));
                            scheme.put(plan.get("Time3").toString(), Float.parseFloat((String) plan.get("Temperature3")));
                            scheme.put(plan.get("Time4").toString(), Float.parseFloat((String) plan.get("Temperature4")));
                            scheme.put(plan.get("Time5").toString(), Float.parseFloat((String) plan.get("Temperature5")));
                            scheme.put(plan.get("Time6").toString(), Float.parseFloat((String) plan.get("Temperature6")));
                            schemes.add(scheme);
                        }
                        //requestData.sentPustParameter(unit.getIndex(), schemes);

                        PublicUnitWeekDaySchemeSelect schemeSelect = new PublicUnitWeekDaySchemeSelect();
                        Map weekPlans = (Map) param.get("WeekPlans");
                        schemeSelect.setMonday(Integer.parseInt((String) weekPlans.get("Monday")));
                        schemeSelect.setTuesday(Integer.parseInt((String) weekPlans.get("Tuesday")));
                        schemeSelect.setWednesday(Integer.parseInt((String) weekPlans.get("Wednesday")));
                        schemeSelect.setThursday(Integer.parseInt((String) weekPlans.get("Thursday")));
                        schemeSelect.setFriday(Integer.parseInt((String) weekPlans.get("Friday")));
                        schemeSelect.setSaturday(Integer.parseInt((String) weekPlans.get("Saturday")));
                        schemeSelect.setSunday(Integer.parseInt((String) weekPlans.get("Sunday")));
                        //requestData.sentPuwdssParameter(unit.getIndex(), schemeSelect);

                        PublicUnitDiyHolidaySchemeSelect holidaySchemeSelect = new PublicUnitDiyHolidaySchemeSelect();
                        holidaySchemeSelect.setCount(8);
                        PublicUnitDiyHolidaySchemeSelect.SchemeSelect holidayScheme;
                        for (int i = 1; i <= 8; i++) {
                            Map holidayPlan = (Map) param.get("HolidaysPlans" + i);
                            if (holidayPlan != null) {
                                holidayScheme = new PublicUnitDiyHolidaySchemeSelect.SchemeSelect(
                                        i, holidayPlan.get("StartDateTime").toString(),
                                        holidayPlan.get("EndDateTime").toString(), Integer.parseInt((String) holidayPlan.get("name"))
                                );
                                holidaySchemeSelect.setSchemeSelects(holidayScheme, i - 1);
                            }
                        }

                        strategy = new PublicUnitAdjustStrategy();
                        strategy.setHandAuto(0);
                        strategy.setControlObject(controlObject((String) param.get("controlObject")));
                        requestData.sentTimeFrameControl(unit.getIndex(), strategy, schemes, schemeSelect, holidaySchemeSelect);
                        break;
                    case 3:
                        Map pidMap = (Map) param.get("PID");
                        PID pid = new PID();
                        pid.setP((byte) (int) pidMap.get("P"));
                        pid.setI((byte) (int) pidMap.get("I"));
                        pid.setD((byte) (int) pidMap.get("D"));
                        requestData.sentPidParameter(unit.getIndex(), pid);
                        break;
                    case 4:
                        Map<String, Integer> valveUpDown = (Map<String, Integer>) param.get("valveUpDown");
                        PublicUnitOpeningLimit limit = new PublicUnitOpeningLimit();
                        limit.setUpLimit(valveUpDown.get("valveUp"));
                        limit.setDownLimit(valveUpDown.get("valveDown"));
                        requestData.sentPuolParameter(unit.getIndex(), limit);
                        break;
                    default:

                }
                log.info("集中器:{} 开始下发楼站阀门控制指令...", serial());
                requestData.setRemain(unit);
                sendRequest(requestData, function);
            }
        }
    }

    @Override
    public void sentPublicUnitModel(List<Node> valves, boolean model, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valves);
            return;
        }
        for (Node valve : valves) {
            if (valve != null) {
                int addr = valve.getSerial().length() > 2 ? Integer.parseInt(valve.getSerial().substring(valve.getSerial().length() - 3)) : Integer.parseInt(valve.getSerial());
                ModbusPoint point = new ModbusPoint(0, "model",
                        ModbusDataType.Modbus_Int16, 40033, ByteOrder.AB, 0);
                point.constructValue(model ? new BigDecimal(1) : new BigDecimal(0));
                ModbusWriteRequestData requestData = new ModbusWriteRequestData(addr,
                        ModbusFunctionCode.WRITE_REGISTERS, point.getRegisterAddress(),
                        Collections.singletonList(point), ModbusType.MODBUS_RTU);
                sendForwardString(valve, valve.getPort(), "2400", "E", requestData.getData(), new TaskFunction() {
                    @Override
                    public void setStatus(Object o) {
                    }

                    @Override
                    public void doApply(CommStatus status, Object data) {
                        List<TransForwardReply> list = (List<TransForwardReply>) data;
                        byte[] bytes = (byte[]) list.get(0).getContent();
                        if (bytes.length == 8) {
                            if (bytes[0] == addr && bytes[1] == 16 &&
                                    bytes[3] == 33 && bytes[5] == 1) {
                                function.doApply(CommStatus.OK, new RunaConcenAffirmDeny(true));
                                return;
                            }
                        }
                        function.doApply(CommStatus.OK, new RunaConcenAffirmDeny(false));
                    }

                    @Override
                    public Object waitResult() {
                        return null;
                    }

                    @Override
                    public String getSerial() {
                        return null;
                    }

                    @Override
                    public int getPriority() {
                        return 0;
                    }
                });
            }
        }
    }


    private int controlObject(String object) {
        String s = ValveVO.ModelEnum.开度.toString();
        if (object.equalsIgnoreCase(ValveVO.ModelEnum.开度.toString())) {
            return 1;
        } else if (object.equalsIgnoreCase(ValveVO.ModelEnum.室温.toString())) {
            return 2;
        } else if (object.equalsIgnoreCase(ValveVO.ModelEnum.流速.toString())) {
            return 3;
        } else if (object.equalsIgnoreCase(ValveVO.ModelEnum.供回温差.toString())) {
            return 4;
        } else if (object.equalsIgnoreCase(ValveVO.ModelEnum.回温.toString())) {
            return 5;
        } else if (object.equalsIgnoreCase(ValveVO.ModelEnum.供温.toString())) {
            return 6;
        }
        log.error("阀门模式对应失败：" + object);
        return 0;
    }

    public void sentMeterReadingCycleParameter(MeterReadingCycle meterReadingCycle, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, null);
            return;
        }
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.sentMeterReadingCycleParameter(meterReadingCycle);
        log.info("集中器:{} 开始下发抄表周期指令...", serial());
        sendRequest(requestData, function);
    }

    @Override
    public void sentExtendParam(ExtendParam param, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, null);
            return;
        }
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.sentExtendParam(param);

        log.info("集中器:{} 开始下发扩展参数指令...", serial());
        sendRequest(requestData, function);
    }

    @Override
    public void readPublicUnitRecord(Node valve, Integer model, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valve);
            return;
        }
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        switch (model) {
            case 1:
                requestData.readPuasParameter(valve.getIndex());
                break;
            case 2:
                requestData.readTimeFrameControl(valve.getIndex());
                break;
            case 3:
                requestData.readPidParameter(valve.getIndex());
                break;
            case 4:
                requestData.readPuolParameter(valve.getIndex());
                break;
            default:
        }
        requestData.setRemain(valve);
        log.info("集中器:{} 开始下发读取楼站阀门参数指令...", serial());
        sendRequest(requestData, function);
    }

    @Override
    public void readPublicUnitAndMeter(Node valve, Node meter, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valve);
        }
        if (valve != null) {
            String[] meterValve = new String[2];
            meterValve[0] = meter.getSerial();
            meterValve[1] = valve.getSerial();
            RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
            requestData.meterPublicUnitData(meter.getIndex(), valve.getIndex());
            requestData.setRemain(valve);
            sendRequest(requestData, function);
        }
    }

    public void sendForwardString(Node node, int port, String baud, String parity, byte[] hex, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, null);
        }
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.transparentForwarding(port, baud, parity, 2, 20, hex);
        requestData.setRemain(node);
        sendRequest(requestData, function);
    }

    public void temperatureCompensation(List<Node> valves, Float comp, TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, valves);
            return;
        }
        for (Node valve : valves) {
            RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
            TemperatureCompensation compensation = new TemperatureCompensation();
            compensation.setTemperatureCompensation(comp);
            requestData.houseOnOffWriteTempComp(valve.getIndex(), compensation);
            requestData.setRemain(valve);
            sendRequest(requestData, function);
        }
    }

    public void readMeterReadingCycleParameter(TaskFunction function) {
        if (!online()) {
            function.doApply(CommStatus.OffLine, null);
            return;
        }
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.readMeterReadingCycleParameter();
        log.info("集中器:{} 开始下发读抄表周期指令...", serial());
        sendRequest(requestData, function);
    }

    public void readTerminalVersion() {
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.readTerminalVersion();
        log.info("集中器:{} 开始下发读版本信息指令...", serial());
        sendRequest(requestData, new SelfTaskFunction(serial()));
    }

    public void readSimCardIccid() {
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.extendData(4);
        log.info("集中器:{} 开始下发读IccID指令...", serial());
        sendRequest(requestData, new SelfTaskFunction(serial()));
    }

    public void timing() {
        RunaConcenRequestData requestData = new RunaConcenRequestData(serial());
        requestData.timing(new Date());
        log.info("集中器:{} 开始下发校时指令...", serial());
        sendRequest(requestData, new SelfTaskFunction(serial(), "下发校时指令"));
    }

    public static void main(String[] args) {
        RunaConcenRequestData requestData = new RunaConcenRequestData("13980854");
//        MeterReadingCycle meterReadingCycle = newComm MeterReadingCycle(24);
//        requestData.sentMeterReadingCycleParameter(meterReadingCycle);
//        requestData.readMeterReadingCycleParameter();
//        requestData.extendData(4);
        ArrayList<Integer> integers = new ArrayList<>();
//        integers.add(33);
//        requestData.heatMeterData(integers);
//        requestData.extendData(4);
        requestData.measurTemperaturePanelData(Collections.singletonList(29));
        byte[] data = requestData.getData();
        String s = ByteUtil.byteToHexString(data, 0, data.length);
        System.out.println(s);
    }

}
